/*
 * PPMC.cpp
 *
 *  Created on: 07/06/2010
 *      Author: administrador
 */

#include "PPMC.h"
#include "ContextoMenos1.h"
#include <stdio.h>

string const PPMC::claveContextoOrdenCero = "\b";

PPMC::PPMC(int ordenMaximo, EstrategiaPPMC* estrategia)
{
	this -> ordenMaximo = ordenMaximo;
	this -> cantidadCaracteresLeidos = 0;

	/* Inicializo la estrategia del PPMC y la clave del contexto de orden 0 */
	this -> estrategia = estrategia;

	/* Agrego el contexto de orden 0 */

	Contexto* contextoInicial = new Contexto(claveContextoOrdenCero);

	this -> estrategia -> agregarContexto(contextoInicial);
}

PPMC::~PPMC()
{
	delete this -> estrategia;
}

string PPMC::obtenerClaveContexto(char* caracteresLeidos, int ordenActual){

	string claveContexto;

	if (ordenActual == 0)
		{
			claveContexto = PPMC::claveContextoOrdenCero;
		}
		else
		{
			int maximoCaracter = 0;

			if (this->ordenMaximo < this->cantidadCaracteresLeidos){

				 maximoCaracter = this->ordenMaximo ;
			}
			else{

				maximoCaracter = this->cantidadCaracteresLeidos;
			}

			int i = maximoCaracter - ordenActual;
			while (i  < maximoCaracter)
			{
				claveContexto += caracteresLeidos[i];
				i++;
			}
		}

	return claveContexto;
}

int PPMC::obtenerOrdenActual()
{
	if (this -> ordenMaximo < this -> cantidadCaracteresLeidos)
		return this -> ordenMaximo;
	else
		return this -> cantidadCaracteresLeidos;
}

Contexto* PPMC::obtenerContexto(char* caracteresLeidos, int ordenActual)
{

	if (ordenActual<0){

		return new ContextoMenos1();
	}

	string claveContexto = this->obtenerClaveContexto(caracteresLeidos,ordenActual);

	Contexto *buscado = this -> estrategia -> buscarContexto(claveContexto);

	if (!buscado){

		buscado = new Contexto(claveContexto);

		this->estrategia->agregarContexto(buscado);

	}	
	
	return buscado ;
}

void PPMC::agregarContexto(char* caracteresLeidos, char caracterActual, int ordenActual)
{
	string claveContexto;

	unsigned int i;

	if (this -> ordenMaximo < this -> cantidadCaracteresLeidos)
	{
		for (i = this -> ordenMaximo - ordenActual; i < this -> ordenMaximo; i++)
			claveContexto += caracteresLeidos[i];
	}
	else
	{
		for (i = this -> cantidadCaracteresLeidos - ordenActual; i < this -> cantidadCaracteresLeidos; i++)
			claveContexto += caracteresLeidos[i];
	}

	claveContexto += caracterActual;



}

char PPMC::interpretarCaracter(unsigned int caracterNumero)
{
	//todo implementar
	return 'a';
}

string PPMC::buscarClaveAnterior(string claveActual)
{
	string claveAnterior;

	claveAnterior = claveActual.substr(1,claveActual.size());

	return claveAnterior;
}

string PPMC::buscarClaveSiguiente(string claveActual)
{
	string claveAnterior;
	int desde;

	if(claveActual == PPMC::claveContextoOrdenCero)
	{
		desde = CtxMaximo->getClave().size() - 1;

	}else
	{
		desde = CtxMaximo->getClave().size() - claveActual.size() - 1 ;
	}

	claveAnterior = CtxMaximo -> getClave().substr(desde, CtxMaximo->getClave().size());

	return claveAnterior;
}

Contexto* PPMC::getContextoAnterior()
{

	string claveActual = CtxActual -> getClave();

	if(CtxActual -> getOrden() == 0)
	{
		//TODO: deberia retornar el -1
	}else
	{
		if(CtxActual -> getOrden() == 1)
		{

			return this->estrategia->buscarContexto(PPMC::claveContextoOrdenCero);

		}else
		{
			return this -> estrategia -> buscarContexto(buscarClaveAnterior(claveActual));
		}
	}

	return NULL;
}

Contexto* PPMC::getContextoSiguiente()
{
	string claveActual = CtxActual -> getClave();

	if(CtxActual -> getOrden() == -1)
	{
		return this->estrategia->buscarContexto(PPMC::claveContextoOrdenCero);

	}else
	{
		return this -> estrategia -> buscarContexto(buscarClaveSiguiente(claveActual));
	}

	return NULL;
}

void PPMC::agregarCaracterLeido(char caracterActual, char* caracteresLeidos)
{
	if (this -> ordenMaximo <= this -> cantidadCaracteresLeidos)
	{
		for (unsigned int i = 1; i < this -> ordenMaximo; i++)
			caracteresLeidos[i-1] = caracteresLeidos[i];

		caracteresLeidos[this -> ordenMaximo - 1] = caracterActual;
		caracteresLeidos[this->ordenMaximo]='\0';
	}
	else
		caracteresLeidos[this -> cantidadCaracteresLeidos] = caracterActual;
		caracteresLeidos[this->cantidadCaracteresLeidos+1] = '\0';
}

void PPMC::comprimir(char* rutaArchivo)
{
	FILE* archivo;
	archivo = fopen(rutaArchivo, "r");

	/* Verifico que el archivo sea valido */
	if(archivo == NULL)
	{
		throw ArchivoInvalidoException(rutaArchivo);
	}

	char caracterActual;

	int largoArray = this->ordenMaximo;

	char *caracteresLeidos = new char[largoArray+1];

	caracteresLeidos[0]='\0';

	int ordenActual;
	bool emitidoESCAPE;

	Contexto* contextoActual;

	/* variable utilizada para saber cuantos caracteres se restan en la exclusion */
//	int exclusion = 0;

	caracterActual = fgetc(archivo);

	cout <<"\n------------Comienza la compresion - Orden Máximo "<< this->ordenMaximo <<" --------------------------\n\n";

	while (caracterActual != EOF)
	{
		cout << "---------------------Comprimiendo el caracter \"" << caracterActual << "\" -- Caracteres Leidos \""<< caracteresLeidos << "\" Cantidad leidos ---------------------" <<"\n\n";

		ordenActual = this -> obtenerOrdenActual();

		emitidoESCAPE = true;

		while ((ordenActual >= -1) && (emitidoESCAPE))
		{
			contextoActual = this -> obtenerContexto(caracteresLeidos, ordenActual);

			cout << "Orden Actual: " << ordenActual <<"\n";

			emitidoESCAPE = contextoActual -> emitir(caracterActual);

			if ((emitidoESCAPE))
			{
				contextoActual->agregarDistribucion(caracterActual);
			}

			if (ordenActual>=0){
				this->estrategia->actualizarContexto(contextoActual);
			}

			delete contextoActual;

			ordenActual--;

		}
			cout << "**********************************************************\n\n";

//		if (emitidoESCAPE)
//		{
//			this -> emitir(caracterActual, exclusion);
//
//			exclusion++;
//		}

		this -> agregarCaracterLeido(caracterActual, caracteresLeidos);

		this -> cantidadCaracteresLeidos++;

		caracterActual = fgetc(archivo);
	}

	fclose(archivo);
}

char PPMC::actualizarDescompresion(unsigned int caracterNumero)
{
	char aEmitir;
	string claveAux;
	Contexto* CtxAux = NULL;

	//se ve a que caracter corresponde dentro del CtxActual
	aEmitir = interpretarCaracter(caracterNumero);

	if(aEmitir == Distribucion::CARACTER_ESCAPE)
	{
		//Se actualiza el CtxActual agregando el ESC. Si es el -1 no hace nada
		CtxActual -> acumularCaracter(aEmitir);

		//Se actualiza la lista de Exclusion, se agregan los caracteres que no esten en la lista y que no sean ESC
		//actualizarExclusion(CtxActual);

		//Si CtxActual = 0 -> CtxAnterior = -1. Si CtxActual = 'xyz' -> CtxAnterior = 'yz'
		CtxActual = getContextoAnterior();

		return Distribucion::CARACTER_ESCAPE;
		//no se deberia escribir nada en el archivo

	}else{
		//Encontre el caracter que queria
		while(CtxActual -> getOrden() <= CtxMaximo -> getOrden())
		{

			if(CtxActual -> getOrden() != -1)
			{

				//Se actualiza el CtxActual agregando el caracter. Si es el -1 no hace nada
				CtxActual -> acumularCaracter(aEmitir);

				if(CtxActual -> getOrden() != 0)
				{
					claveAux = aEmitir + CtxActual -> getClave();
				}else
				{
					claveAux = aEmitir;
					//pues la clave del contexto 0 es vacia
				}

				//Creo que para Ctx 0 quedaria solo claveAux = aEmitir
				if((int)claveAux.size() <= ordenMaximo)
				{
					CtxAux = new Contexto(claveAux);
					//agregarContexto(CtxAux);
					//solo en caso de que no exista
				}

			}


			//Esto se pregunta porque cuando obtenga el siguiente me aseguro que haya siguiente y no sigo iterando sin salida
			if(CtxActual -> getClave() != CtxMaximo -> getClave())
			{
				CtxActual = getContextoSiguiente();
				//al reves que el anterior, lo obtiene usando el actual y el	maximo
			}else{
				CtxActual = CtxAux;
			}
		}

		//Finalizo y dejo listo para la proxima letra
		CtxActual = CtxAux;
		CtxMaximo = CtxActual;
		//listaExclusion -> limpiar();
		return aEmitir;
	}

}

